#include "kernel.h"

/* I/O APIC Code from xv6 */

#define REG_ID     0x00  // Register index: ID
#define REG_VER    0x01  // Register index: version
#define REG_TABLE  0x10  // Redirection table base

// The redirection table starts at REG_TABLE and uses
// two registers to configure each interrupt.  
// The first (low) register in a pair contains configuration bits.
// The second (high) register contains a bitmask telling which
// CPUs can serve that interrupt.
#define INT_DISABLED   0x00010000  // Interrupt disabled
#define INT_LEVEL      0x00008000  // Level-triggered (vs edge-)
#define INT_ACTIVELOW  0x00002000  // Active low (vs high)
#define INT_LOGICAL    0x00000800  // Destination is CPU id (vs APIC ID)

static uint32_t
ioapic_read(ioapic_mmio_t *ioapic, int reg)
{
	 ioapic->reg = reg;
	 return ioapic->data;
}

static void
ioapic_write(ioapic_mmio_t *ioapic, int reg, uint32_t data)
{
	 ioapic->reg = reg;
	 ioapic->data = data;
}

struct ioapic_t ioapics[IOAPIC_MAX_COUNT];

void
ioapic_init(void)
{
	 int i, id, maxintr;
	 int ioapic_id;
	 ioapic_mmio_t *ioapic;
	 
	 for (ioapic_id = 0; ioapic_id != sysconf.ioapic_count; ++ ioapic_id)
	 {
		  kprintf("process ioapic %d\n", ioapic_id);
		  
		  BEGIN_TPAGE(ioapics[ioapic_id].phys, ioapic);
		  
		  maxintr = (ioapic_read(ioapic, REG_VER) >> 16) & 0xFF;
		  id = ioapic_read(ioapic, REG_ID) >> 24;
		  if(id != ioapic_id)
			   kprintf("ioapic_init: id isn't equal to ioapic_id; not a MP\n");

		  // Mark all interrupts edge-triggered, active high, disabled,
		  // and not routed to any CPUs.
		  for(i = 0; i <= maxintr; i++)
		  {
			   ioapic_write(ioapic, REG_TABLE + 2 * i, INT_DISABLED | (IRQ_OFFSET + i));
			   ioapic_write(ioapic, REG_TABLE + 2 * i + 1, 0);
		  }
		  
		  END_TPAGE;
	 }
}

void
ioapic_enable(ioapic_mmio_t *ioapic, int irq, int cpunum)
{
	 // Mark interrupt edge-triggered, active high,
	 // enabled, and routed to the given cpunum,
	 // which happens to be that cpu's APIC ID.
	 ioapic_write(ioapic, REG_TABLE + 2 * irq, IRQ_OFFSET + irq);
	 ioapic_write(ioapic, REG_TABLE + 2 * irq + 1, cpunum << 24);
}
